package org.hoyi.loader;

import java.io.BufferedInputStream;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Method;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Hashtable;
import java.util.Map;
import java.util.jar.JarEntry;
import java.util.jar.JarInputStream;
import java.util.jar.Manifest;

import org.hoyi.util.StreamUtils;

public class JarLoader extends ClassLoader{
    //资源缓存
//    public static Hashtable resources = new Hashtable();
    public static Map<String, byte[]> resMap = new HashMap<>();

    public static JarLoader loader = new JarLoader();

    private static JarLoader instance = null;

	public static JarLoader getInstance() {
		if (JarLoader.instance == null) {
			JarLoader.instance = new JarLoader();
		}
		return JarLoader.instance;
	}

//	#loader.defineClass((String) classNames.get(i), (byte[]) classBuffers.get(i), 0, ((byte[]) classBuffers.get(i)).length);
	public static Class<?> nDefClass(String name, byte[] b, int off, int len) {
		return loader.defineClass(name, b, off, len);
	}

    public static void load(byte[] resource){
//        public static void load(byte[] resource) throws Exception{
        // 主函数所在类全称
//        String mainClassName = "";
        //class资源及实体缓存
        ArrayList classNames = new ArrayList();
        ArrayList classBuffers = new ArrayList();
        // 存储依赖类
        HashMap depends = new HashMap();
        HashMap<String,byte[]> depbuff =new HashMap<>();
        
        // 将byte[]转为JarInputStream
        JarInputStream jar = null;
        try {
        	jar = new JarInputStream(new ByteArrayInputStream(resource));
        }catch (Exception e) {
			// TODO: handle exception
        	System.out.print("error file");
		}

//        Manifest manifest = jar.getManifest();
//        // 当Main-Class被声明时,获得主函数所在类全称
//        if (manifest != null){
//            mainClassName = manifest.getMainAttributes().getValue("Main-Class");
//            System.out.println("MainClassName:" + mainClassName);
//        }else{
//        	System.out.println("return manifest is null");
//        	return null;
//        }
        // 依次获得对应JAR文件中封装的各个被压缩文件的JarEntry


        System.out.println("Jar Entity resource Length:" + resource.length);
       // jar.getNextEntry();

        System.out.println("Jar Entity resource Length 1 :" + resource.length);
        if(jar == null) {
            System.out.println("Jar file is null ");
            return;
        }
        
        JarEntry entry = null;
        try {
            entry = (JarEntry) jar.getNextEntry();
         
		} catch (Exception e) {
			System.out.println( "get entry error");
		}
        while (entry != null){
            // 当找到的entry为class时
            if (entry.getName().toLowerCase().endsWith(".class")){
                // 将类路径转变为类全称
                String name = entry.getName().substring(0, entry.getName().length() - ".class".length()).replace('/', '.');
                try {
                    // 加载该类
                    //--------------------------------------
                   byte[] data = getResourceData(jar);
                   // byte[] data = JNIner.getInstance().currentBytes;
                    // 缓存类名及数据
                    classNames.add(name);
                    classBuffers.add(data);
				} catch (Exception e) {
					System.err.println("cla error:" + name);
				}

            } else{
                // 非class结尾但开头字符为''/''时
                if (entry.getName().charAt(0) == '/'){
                	//--------------------------------------
//                    resources.put(entry.getName(), getResourceData(jar));
                	//resources.put(entry.getName(), JNIner.getInstance().currentBytes);
                // 否则追加''/''后缓存
                } else{
                	try {
		            	//--------------------------------------
		//                        resources.put(entry.getName(), getResourceData(jar));
		//                        resources.put(entry.getName(), getResourceData(jar));
		//                        resources.put(entry.getName(), getResourceData(jar));
		        		String path = entry.getName();
		        		byte[] bytes = getResourceData(jar);// StreamUtils.Create().ReaderResource(path);
		//                		String ctx = new String(bytes);
		//                		System.out.println("add." + path + ",ctx:" + ctx);
		//                        resources.put(path, bytes);
		                resMap.put(path, bytes);
		                //resources.put(entry.getName().replace("/", "."), getResourceData(jar));
		//                        resources.put("/" + entry.getName(), getResourceData(jar));
		//                        resources.put("/" + entry.getName(), JNIner.getInstance().currentBytes);
					} catch (Exception e) {
						System.out.println("set resource " + entry.getName() + " error");
					}
                }
            }
            try {
                entry = jar.getNextJarEntry();	
			} catch (Exception e) {
				System.out.println("next jar error:");
			}
        }
        //当获得的main-class名不为空时
        while (classNames.size() > 0){
            //获得类路径全长
            int n = classNames.size();
            for (int i = classNames.size() - 1; i >= 0; i--){
                try{
//                	Class<?> exsitclass = loader.findLoadedClass((String) classNames.get(i));
//                	System.out.println("exsitclass");
//                	System.out.println(exsitclass);
//                	if(exsitclass == null) {
                		 //查询指定类
                        loader.defineClass((String) classNames.get(i), (byte[]) classBuffers.get(i), 0, ((byte[]) classBuffers.get(i)).length);
                        //获得类名
                        String pkName = (String) classNames.get(i);
                        if (pkName.lastIndexOf('.') >= 0){
                            pkName = pkName.substring(0, pkName.lastIndexOf('.'));
                            if (loader.getPackage(pkName) == null){
                                loader.definePackage(pkName, null, null, null, null, null, null, null);
                            }
                        }
//                	}
                    //查询后删除缓冲
                    classNames.remove(i);
                    classBuffers.remove(i);
//                } catch (ClassNotFoundException e) {
//                	e.printStackTrace();
				} catch (NoClassDefFoundError e){
                    depends.put((String) classNames.get(i), e.getMessage().replaceAll("/", "."));
                    // 把未定义成功的类和buffer存起来，继续定义.
                    depbuff.put((String) classNames.get(i), (byte[]) classBuffers.get(i));
                    System.out.println("NoClassDefFoundError:" + classNames.get(i) + ", dep is:" +  e.getMessage().replaceAll("/", "."));
                } catch (UnsupportedClassVersionError e){
                    //jre版本错误提示
//                    throw new UnsupportedClassVersionError(classNames.get(i)
//                            + ", " + System.getProperty("java.vm.name") + " "
//                            + System.getProperty("java.vm.version") + ")");
                	System.out.println("unsupport class version:" + classNames.get(i));
                }
            }
            if (n == classNames.size()){
                for (int i = 0; i < classNames.size(); i++){
                    System.err.println("No Class Def->:"  + classNames.get(i));
                    String className = (String) classNames.get(i);
                    while (depends.containsKey(className)){
                        className = (String) depends.get(className);
//                        byte[] bufs = depbuff.get(className);
//                        try {
//                            loader.defineClass(className, bufs, 0, bufs.length);
//                            System.out.println("redefine:" + className);
//                            depends.remove(className);
//                            depbuff.remove(className);
//						} catch (Exception e) {
//							System.out.println("try.redef cla error:" + className);
//							// TODO: handle exception
//						}
                    }
                }
                break;
            }
//            if (n == classNames.size()){
//                for (int i = 0; i < classNames.size(); i++){
//                    System.err.println("No Class Def->:"  + classNames.get(i));
//                    String className = (String) classNames.get(i);
//                    while (depends.containsKey(className)){
//                        className = (String) depends.get(className);
//                    }
//                }
//                break;
//            }
        }
        System.err.println("finished by classNames:" + classNames.size());
//        JarLoader.GetRes("WebRoot/index.hoyip.html");
        try{
            //加载
            Thread.currentThread().setContextClassLoader(loader);
            // 获得指定类,查找其他类方式相仿

//            System.out.println("调用方法:" + mainClassName);
//            return Class.forName(mainClassName, true, loader);
//        } catch (ClassNotFoundException e){
        } catch (Exception e){
//            String className = mainClassName;
//            while (depends.containsKey(className)){
//                className = (String) depends.get(className);
//            }
//            throw new ClassNotFoundException(className);
        }
    }

    public static Object GetRes(String path) {
    	byte[] bts = resMap.get(path);
    	String ctx = new String(bts);
 		System.out.println("CTX:" + ctx);
 		return bts;
    }

    /** *//**
     * 获得指定路径文件的byte[]形式
     * @param name
     * @return
     */
    final static public byte[] getDataSource(String name){
        FileInputStream fileInput;
        try{
            fileInput = new FileInputStream(new File(name));
        } catch (FileNotFoundException e){
            fileInput = null;
        }
        BufferedInputStream bufferedInput = new BufferedInputStream(fileInput);
        return getDataSource(bufferedInput);
    }

    /** *//**
     * 获得指定InputStream的byte[]形式
     * @param name
     * @return
     */
    final static public byte[] getDataSource(InputStream is){
        if (is == null){
            return null;
        }
        ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
        byte[] arrayByte = null;
        try{
            byte[] bytes = new byte[8192];
            bytes = new byte[is.available()];
            int read;
            while ((read = is.read(bytes)) >= 0){
                byteArrayOutputStream.write(bytes, 0, read);
            }
            arrayByte = byteArrayOutputStream.toByteArray();
        } catch (IOException e){
            return null;
        } finally{
            try{
                if (byteArrayOutputStream != null){
                    byteArrayOutputStream.close();
                    byteArrayOutputStream = null;
                }
                if (is != null){
                    is.close();
                    is = null;
                }

            } catch (IOException e){
            }
        }
        return arrayByte;
    }

    /** *//**
     * 获得指定JarInputStream的byte[]形式
     * @param jar
     * @return
     * @throws IOException
     */
     final static private byte[] getResourceData(JarInputStream jar)
            throws IOException{
        ByteArrayOutputStream data = new ByteArrayOutputStream();
        byte[] buffer = new byte[8192];
        int size;
        while (jar.available() > 0){
            size = jar.read(buffer);
            if (size > 0){
                data.write(buffer, 0, size);
            }
        }
        return data.toByteArray();
    }

     /** *//**
      * 重载的getResource,检查是否重复包含
      */
//    public URL getResource(String name){
//        if (resources.containsKey("/" + name)){
//            try{
//                return new URL("file:///" + name);
//            } catch (MalformedURLException e){
//                e.printStackTrace();
//            }
//        }
//        return super.getResource(name);
//    }

    /** *//**
     * 执行指定class类
     * @param clz
     * @param methodName
     * @param args
     */
    public static void callVoidMethod(Class clz, String methodName,
            String[] args){
        Class[] arg = new Class[1];
        arg[0] = args.getClass();
        try{
            Method method = clz.getMethod(methodName, arg);
            Object[] inArg = new Object[1];
            inArg[0] = args;
            method.invoke(clz, inArg);
        } catch (Exception e){
            System.err.println(e.getMessage());
        }
    }

    public static void callVoidMethod(String mcn, String methodName, String[] args){
		try {
			Class<?> cla = Class.forName(mcn, true, JarLoader.loader);

			Method mainMethod  =  cla.getMethod("runs", String[].class);//  .getMethod("main", "String[].class");
//				Method mainMethod  =  cla.getMethod("main", String[].class);//  .getMethod("main", "String[].class");

			mainMethod.invoke(null,  (Object)new String[]{""});
		} catch (Exception e) {
			e.printStackTrace();
		}
		System.out.println("This is testr.loaded.congulations.");
    }

     /** *//**
      * 重载的getResourceAsStream,检查是否重复包含
      */
//    public InputStream getResourceAsStream(String name){
//        if (name.charAt(0) == '/'){
//            name = name.substring(1);
//        }
//        if (resources.containsKey("/" + name)){
//            return new ByteArrayInputStream((byte[]) resources.get("/" + name));
//        }
//        return super.getResourceAsStream(name);
//    }

}
